﻿using IOP.SgrA;
using IOP.SgrA.ECS;
using IOP.SgrA.SilkNet.Vulkan;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.IO;
using System.Numerics;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using IOP.Extension.DependencyInjection;
using IOP.ISEA;
using Silk.NET.Vulkan;

namespace VkSample113;

[ModulePriority(ModulePriority.Assets)]
public class AssetsModule : VulkanModule, IVulkanRenderWapperModule
{
    public VulkanRenderWapper RenderWapper { get; set; }
    [Autowired]
    private ILogger<AssetsModule>? _Logger { get; set; }

    protected override async Task Load(VulkanGraphicsManager graphicsManager)
    {
        try
        {
            var basePath = AppContext.BaseDirectory;
            var modelsPath = Path.Combine(basePath, "Assets", "Models");
            var texPath = Path.Combine(basePath, "Assets", "Textures");
            var lDevice = graphicsManager.VulkanDevice;

            ISEADocument document = new();
            await document.Load(Path.Combine(AppContext.BaseDirectory, "test113.isea"));
            var renders = await graphicsManager.CreateRenderObjectsFromISEA<RenderObject>(document);

            await CreateLand(renders, graphicsManager, texPath);
            await CreateSea(renders, graphicsManager, texPath);
            await CreateSky(renders, graphicsManager, texPath);
            await CreateTree(renders, graphicsManager, texPath);
        }
        catch (Exception e)
        {
            _Logger?.LogError(e.Message + "\r\n" + e.StackTrace);
        }
    }

    private async Task CreateLand(RenderObject[] renders, VulkanGraphicsManager graphicsManager, string texPath)
    {
        var lDevice = graphicsManager.VulkanDevice;

        var land = renders.Where(x => x.Name == "Land").First();
        BnhdtVulkanTextureData landHeight = new BnhdtVulkanTextureData();
        await landHeight.Load(File.OpenRead(Path.Combine(texPath, "land1.bnhdt")));
        byte[] pData = new byte[landHeight.BytesSize];
        Marshal.Copy(landHeight.Buffer, pData, 0, (int)landHeight.BytesSize);
        LandData landData = new LandData();
        landData.GenData((int)landHeight.Width, (int)landHeight.Height, pData);
        var landMesh = graphicsManager.CreateEmptyMesh<VRO>("LandMesh").CreateVecticesBuffer(landData.Data, (uint)landData.VCount, lDevice, SharingMode.Exclusive);
        land.AddComponent(landMesh);
        land.GetTransform().Position = new Vector3(0.0f, -100.0f, 0.0f);

        graphicsManager.TryGetRenderGroup("Land", out IRenderGroup landGroup);
        var landArchetype = ContextManager.CreateArchetype("Land", new LandComponent());
        ContextManager.CreateContexts(1, landArchetype, landGroup, land);
    }

    private Task CreateSea(RenderObject[] renders, VulkanGraphicsManager graphicsManager, string texPath)
    {
        var lDevice = graphicsManager.VulkanDevice;

        var sea = renders.Where(x => x.Name == "Sea").First();
        FlatData flatData = new FlatData();
        flatData.GenVertexData();
        var seaMesh = graphicsManager.CreateEmptyMesh<VRO>("SeaMesh").CreateVecticesBuffer(flatData.Data, (uint)flatData.VCount, lDevice, SharingMode.Exclusive);
        sea.AddComponent(seaMesh);
        var uniform = graphicsManager.CreateUniformBufferTexture("SeaUniform", 8, binding: 0, setIndex: 0);
        sea.AddComponent(uniform);
        graphicsManager.CreateAnimation<RenderObject, WaveAnimation>(sea, "WaveAnimation");
        var trans = sea.GetTransform();
        trans.Position = new Vector3(0, Constant.water_height, 0);
        trans.Rotation = Quaternion.CreateFromAxisAngle(Vector3.UnitX, -90f * MathF.PI / 180.0f);

        graphicsManager.TryGetRenderGroup("Sea", out IRenderGroup seaGroup);
        var landArchetype = ContextManager.CreateArchetype("Sea", new SeaComponent());
        ContextManager.CreateContexts(1, landArchetype, seaGroup, sea);
        return Task.CompletedTask;
    }

    private Task CreateSky(RenderObject[] renders, VulkanGraphicsManager graphicsManager, string texPath)
    {
        var lDevice = graphicsManager.VulkanDevice;

        var sky = renders.Where(x => x.Name == "Sky").First();
        SkyBallData skyBall = new SkyBallData();
        skyBall.GenVertexData();
        var skyMesh = graphicsManager.CreateEmptyMesh<VRO>("SkyMesh").CreateVecticesBuffer(skyBall.Data, (uint)skyBall.VCount, lDevice, SharingMode.Exclusive);
        sky.AddComponent(skyMesh);
        graphicsManager.TryGetRenderGroup("Common", out IRenderGroup group);
        var skyArchetype = ContextManager.CreateArchetype("Sky", new SkyComponent());
        ContextManager.CreateContexts(1, skyArchetype, group, sky);
        return Task.CompletedTask;
    }

    private Task CreateTree(RenderObject[] renders, VulkanGraphicsManager graphicsManager, string texPath)
    {
        var lDevice = graphicsManager.VulkanDevice;
        graphicsManager.TryGetRenderGroup("TreeTrunk", out IRenderGroup trunkGroup);
        var trunkArchetype = ContextManager.CreateArchetype("Trunk", new TrunkComponent());

        var trunk = renders.Where(x => x.Name == "TreeTrunk").First();
        var leave = renders.Where(x => x.Name == "TreeLeaves").First();
        TreeTrunkData data = new TreeTrunkData();
        TreeLeavesData leavesData = new TreeLeavesData();
        data.GenVertexData(Constant.bottom_radius, Constant.joint_height, Constant.joint_num, Constant.joint_available_num);
        leavesData.GenVertexData(Constant.leaves_width, Constant.leaves_height, Constant.TREE_HEIGHT);
        var trunkMesh = graphicsManager.CreateEmptyMesh<VRO>("TrunkMesh").CreateVecticesBuffer(data.Data, (uint)data.VCount, lDevice, SharingMode.Exclusive);
        var leafMesh = graphicsManager.CreateEmptyMesh<VRO>("LeafMesh").CreateVecticesBuffer(leavesData.Data, (uint)leavesData.VCount, lDevice, SharingMode.Exclusive);
        trunk.AddComponent(trunkMesh);
        leave.AddComponent(leafMesh);
        var uniform = graphicsManager.CreateUniformBufferTexture("TreeUniform", 8, binding: 0, setIndex: 0);
        trunk.AddComponent(uniform);
        for(int i = 0; i < 6; i++)
        {
            var copy = leave.DeepCopy(graphicsManager, $"TreeLeaf-{i}");
            LeafTransform init = new();
            init.InitPosition = new Vector3(Constant.leaves_width / 2 * MathF.Cos(toRadians(60.0f * i)),
                Constant.LEAVE_CENTER_HEIGHT, Constant.leaves_height / 2 * MathF.Sin(toRadians(60.0f * i)));
            init.InitRotation = Quaternion.CreateFromAxisAngle(Vector3.UnitY, toRadians(-60.0f * i));
            copy.AddComponent(init);
            trunk.AddChildren(copy);
        }

        ContextManager.CreateContexts(4, trunkArchetype, trunkGroup, trunk, true);
        return Task.CompletedTask;
    }

    protected override Task Unload(VulkanGraphicsManager manager)
    {
        return Task.CompletedTask;
    }

    private float toRadians(float degree)
    {//角度转换成弧度的方法
        return (float)degree * MathF.PI / 180;
    }
}
